﻿namespace WcfComposition
{
    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel.Composition.Hosting;
    using System.Diagnostics.Contracts;
    using System.Linq;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Description;
    using System.ServiceModel.Dispatcher;

    /// <summary>
    /// A service behavior created to insert a instance provider that utilizes
    /// MEF to compose the service parts.
    /// </summary>
    /// <remarks>Implements <see cref="IServiceBehavior"/>.</remarks>
    public class ComposingServiceBehavior : IServiceBehavior
    {
        #region Fields

        private readonly CompositionContainer container;
        private readonly Type serviceType;

        #endregion

        #region Constructors

        public ComposingServiceBehavior(Type type, CompositionContainer container)
        {
            Contract.Requires( container != null );
            Contract.Requires( type != null );

            this.container = container;
            this.serviceType = type;
        }

        #endregion

        #region IServiceBehavior Members

        /// <summary>
        /// Applies the dispatch behavior to the service.
        /// </summary>
        /// <param name="serviceDescription">The <see cref="ServiceDescription">service description</see>.</param>
        /// <param name="serviceHostBase">The <see cref="ServiceHostBase">service host</see>.</param>
        /// <remarks>The base class has no implementation.</remarks>
        public virtual void ApplyDispatchBehavior( ServiceDescription serviceDescription, ServiceHostBase serviceHostBase )
        {
            foreach ( var dispatcher in serviceHostBase.ChannelDispatchers.OfType<ChannelDispatcher>() )
            {
                foreach ( var endpoint in dispatcher.Endpoints )
                    endpoint.DispatchRuntime.InstanceProvider = new ComposingInstanceProvider( serviceDescription.ServiceType, this.container );
            }
        }

        /// <summary>
        /// Adds the binding parameters to the service.
        /// </summary>
        /// <param name="serviceDescription">The <see cref="ServiceDescription">service description</see>.</param>
        /// <param name="serviceHostBase">The <see cref="ServiceHostBase">service host</see>.</param>
        /// <param name="endpoints">A <see cref="Collection{T}">collection</see> of <see cref="ServiceEndpoint">service endpoints</see>.</param>
        /// <param name="bindingParameters">A <see cref="BindingParameterCollection">collection</see> of service <see cref="Object">binding parameters</see>.</param>
        public virtual void AddBindingParameters( ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters )
        {
        }

        /// <summary>
        /// Validates the behavior against the service.
        /// </summary>
        /// <param name="serviceDescription">The <see cref="ServiceDescription">service description</see>.</param>
        /// <param name="serviceHostBase">The <see cref="ServiceHostBase">service host</see>.</param>
        /// <remarks>The base class has no implementation.</remarks>
        public virtual void Validate( ServiceDescription serviceDescription, ServiceHostBase serviceHostBase )
        {
        }

        #endregion
    }
}
